////////////////////////////////////////////////////////////////////////
//irmm_Raster.h
//This is the description of classes of image library including
//======================================================================
//		Gaussian filtering
//		segmentation of color image using mean shift algorithm
//=======================================================================
//	Author: Zhuowen Tu
////////////////////////////////////////////////////////////////////////
#ifndef _IRMM_RASTERCOLOR_H
#define _IRMM_RASTERCOLOR_H

#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include "mcRaster.h"
#include "Vector.h"

// List of color components; the possible cases are:
#define RedColor		(1<<0)
#define GreenColor		(1<<1)
#define BlueColor		(1<<2)
#define Lightness		(1<<3)
#define Ustar   		(1<<4)
#define Vstar   		(1<<5)
#define FirstChannel    (1<<6)
#define SecondChannel   (1<<7)
#define ThirdChannel    (1<<8)

#define LUV2RGB         10
#define YCrCb2RGB       11


template<class Tp>
class ColorRGB
{
public:
	Tp r;
    Tp g;
    Tp b;

	ColorRGB(){r=0;g=0;b=0;};
	ColorRGB(Tp ir,Tp ig,Tp ib){r=ir;g=ig;b=ib;};

	inline void Set(Tp rc,Tp gc,Tp bc){r=rc;g=gc;b=bc;};
};

class ColorLUV
{
public:
	int	L;
    int	U;
    int	V;

	ColorLUV(){L=0;U=0;V=0;};
	ColorLUV(const int iL, const int iU, const int iV){L=iL; U=iU; V=iV;};

	inline void Set(const int iL, const int iU, const int iV){L=iL; U=iU; V=iV;};
};

#define MAXV 256

////////////////////////////////////////////////////////////
//this is the class of color image with three channels in r, g, b or
//L, U, V spaces
template<class Tp>
class Raster_Color
{
public:
	// construction
	Raster_Color();
	~Raster_Color();

	// operators
	Raster_Color& operator=(Raster_Color<uchar> &I);
	Raster_Color& operator=(Raster_Color<double> &I);
	void Copy(Raster_Color<uchar> &I, irmm_Rect& rect);

	void SetDimension(const int nrows, const int ncols);

	// access
	inline Tp&		R(const int Row, const int Col);
	inline Tp&		G(const int Row, const int Col);
	inline Tp&		B(const int Row, const int Col);
	inline Tp&		R(const long index);
	inline Tp&		G(const long index);
	inline Tp&		B(const long index);

	inline int&		L(const int Row, const int Col){ return LUV.at<cv::Vec3i>(Row, Col)[0]; };
	inline int&		U(const int Row, const int Col){ return LUV.at<cv::Vec3i>(Row, Col)[1]; };
	inline int&		V(const int Row, const int Col){ return LUV.at<cv::Vec3i>(Row, Col)[2]; };
	inline int&		L(const long index){ return LUV.at<cv::Vec3i>(index / _rows, index%_rows)[0]; };
	inline int&		U(const long index){ return LUV.at<cv::Vec3i>(index / _rows, index%_rows)[1]; };
	inline int&		V(const long index){ return LUV.at<cv::Vec3i>(index / _rows, index%_rows)[2]; };

	inline int		gray(const long index);
	inline int		gray(const int Row, const int Col){return gray(cols()*Row + Col);};
	void			GrayImage(Raster<Tp> &image);

	inline long		rows() const {return _rows;};
	inline long		cols() const {return _cols;};
	inline long		size() const {return _rows*_cols;};

	inline bool		Valid(const int j, const int i)
						{if (j>=0&&j<rows()&&i>=0&&i<cols())return true;else return false;}

	inline long		Width(){return cols();};
	inline long		Height(){return rows();};

	virtual double	color_dis(const int index, const double mean_L, const double mean_U, const double mean_V);
	virtual void	Zero(void);

	// manipulation
	virtual void	convert_RGB_LUV(long selects=Lightness | Ustar | Vstar);			// converts RGB space to L*u*v space
	virtual void    convert_LUV_RGB(double *, int *, long select=Lightness | Ustar | Vstar);								// converts L*u*v space to RGB space

	virtual bool    IsColorImage();
	
	//*********end by wtf

	virtual void	CreateLUVspace(void);
	virtual void	SetPixelInLUV(const int row, const int col,
								  const int l, const int u, const int v);

	// for color indexing
	virtual void	MeanVariance(Matrix<int> &mask_label, irmm_Rect &bound,
								 const int num_label,
								 Matrix<double>	*pmeans,
								 Matrix<double> *pvariances,
								 Matrix<long> &counts);
	virtual void	Compute2DColor(Matrix<int> &mask_label, irmm_Rect &bound, const int num_label, Vector<double> *pvc_N);

protected:

	inline int		ShiftColorValues(const long index){return (((((int)R(index))<<8)+(int)G(index))<<8)+(int)B(index);};
	inline int		ShiftColorValues(const int Row, const int Col){return ShiftColorValues(rows() * cols() + Col);};


public:
	cv::Mat				RGB;
	cv::Mat				LUV;
	cv::Mat				EdgeMap;
	/*
	Raster<Tp>		RGB[3];
	Raster<int>		LUV[3];
	Raster<int>   YCrCb[3];       //add by wtf, for YCrCb color space
	Raster<float>   XYZ[3];
	Raster<float>   LAB[3];
	Raster<uchar>	EdgeMap;		//edge map of this color image
	*/
	long			count;			//pixel count as row * col in this image
	

protected:
	int			_rows, _cols;	//row number and column number of this image
};


template<class Tp1, class Tp2>
void CopyRasterColor(Raster_Color<Tp1>& x1,
						 Raster_Color<Tp2>& x2)  // Jiadi
{
	x1.SetDimension(x2.rows(),x2.cols());
	x2.RGB.copyTo(x1.RGB);
}

///////////////////////////////////////////////////////////
// operators
template<class Tp>
Raster_Color<Tp>& Raster_Color<Tp>::operator=(Raster_Color<uchar> &I) // Jiadi
{
	CopyRasterColor(*this,I);
	return *this;
}

template<class Tp>
Raster_Color<Tp>& Raster_Color<Tp>::operator=(Raster_Color<double> &I) // Jiadi
{
	CopyRasterColor(*this,I);
	return *this;
}

template<class Tp>
void Raster_Color<Tp>::Copy(Raster_Color<uchar> &I, irmm_Rect& rect) // Jiadi
{
	SetDimension(rect.Height(), rect.Width());
	for (int Row=rect.top; Row<=rect.bottom; Row++)
		for (int Col=rect.left; Col<=rect.right; Col++)
		{
			RGB.at<Vec<Tp> >(Row - rect.top, Col - rect.left) = I.RGB.at<Vec<Tp> >(Row, Col);
		}
}

///////////////////////////////////////////////////////////
// inline functions
template<class Tp>
inline Tp& Raster_Color<Tp>::R(const int Row, const int Col) // Jiadi
{
	return RGB.at<cv::Vec<Tp, 3> >(Row, Col)[0];
};

template<class Tp>
inline Tp& Raster_Color<Tp>::R(const long index) // Jiadi
{
	return RGB.at<cv::Vec<Tp, 3> >(index/_rows, index%_rows)[0];
};

template<class Tp>
inline Tp& Raster_Color<Tp>::G(const int Row, const int Col) // Jiadi
{
	return RGB.at<cv::Vec<Tp, 3> >(Row, Col)[1];
};

template<class Tp>
inline Tp& Raster_Color<Tp>::G(const long index) // Jiadi
{
	return RGB.at<cv::Vec<Tp, 3> >(index / _rows, index%_rows)[1];
};

template<class Tp>
inline Tp& Raster_Color<Tp>::B(const int Row, const int Col) // Jiadi
{
	return RGB.at<cv::Vec<Tp, 3> >(Row, Col)[2];
};

template<class Tp>
inline Tp& Raster_Color<Tp>::B(const long index) // Jiadi
{
	return RGB.at<cv::Vec<Tp, 3> >(index / _rows, index%_rows)[2];
};

//==========================================================
// Class : Raster_Color
//==========================================================
//----------------------------------------------------------
// construction
//----------------------------------------------------------
////////////////////////////////////////////////////////////
template<class Tp>
Raster_Color<Tp>::Raster_Color() // Jiadi ! not finished
{
	_rows = 0;
	_cols = 0;
	count = 0;
}

////////////////////////////////////////////////////////////
template<class Tp>
Raster_Color<Tp>::~Raster_Color()
{
	int i = 0;
}

template<class Tp>
void Raster_Color<Tp>::SetDimension(const int nrows, const int ncols) // Jiadi
{
	_rows = nrows;
	_cols = ncols;
	count = ((long)_rows) * _cols;

	RGB.create(nrows, ncols, CV_MAKETYPE(DataType<Tp>::type, 3));
}

template<class Tp>
void Raster_Color<Tp>::Zero(void) // Jiadi
{
	LUV.setTo(cv::Scalar::all(0));
	RGB.setTo(cv::Scalar::all(0));

}


//----------------------------------------------------------
// access
//----------------------------------------------------------
template<class Tp>
double Raster_Color<Tp>::color_dis(const int index, const double mean_L,
									 const double mean_U, const double mean_V) // Jiadi
{
	double fL, fU, fV;

	fL = L(index) - mean_L;
	fU = U(index) - mean_U;
	fV = V(index) - mean_V;
	return (fL * fL + fU * fU + fV * fV);
}

//---------------------------------------------------------------------
// manipulation

///////////////////////////////////////////////////////////////////////
// RGB to LUV conversion
static const int	SEC_SIZE=64;  // 2^6

template<class Tp>
void Raster_Color<Tp>::convert_RGB_LUV(long selects) // Jiadi
{
	//cv::Mat temp;

	cv::Mat temp(RGB);
	temp.convertTo(temp, CV_32FC3);
	temp *= 1. / 255;
	cvtColor(temp, LUV, CV_RGB2Luv);

	/*
	cvtColor(RGB, temp, CV_RGB2Luv);
	//cvtColor(RGB, LUV, CV_RGB2Luv);
	
	LUV.create(temp.size(), CV_32SC3);
	for (int y = 0; y < temp.rows; y++) {
		for (int x = 0; x < temp.cols; x++) {
			LUV.at<cv::Vec3i>(y, x)[0] = int(100. / 255. * temp.at<cv::Vec3b>(y, x)[0]);
			LUV.at<cv::Vec3i>(y, x)[1] = int(354. / 255. * temp.at<cv::Vec3b>(y, x)[1] - 134.);
			LUV.at<cv::Vec3i>(y, x)[2] = int(266. / 255. * temp.at<cv::Vec3b>(y, x)[2] - 140.);
		}
	}
	*/
}

///////////////////////////////////////////////////////////////////
// LUV (final_T[]) to RGB (TI[]) conversion
template<class Tp>
void Raster_Color<Tp>::convert_LUV_RGB(double *pLUV, int *pRGB, long selects) // Jiadi
{
	int		i;
	double	R_G_B[3][3] = { {(double)3.2405, (double)-1.5371, (double)-0.4985 },
                            {(double)-0.9693, (double)1.8760, (double)0.0416 },
                            {(double)0.0556, (double)-0.2040, (double)1.0573 }};

	// this condition is always true
	if (selects & Lightness && selects & Ustar && selects & Vstar)
	{
		if(pLUV[0] < 0.1)
        {
			pRGB[0]=0;
			pRGB[1]=0;
			pRGB[2]=0;
		}
		else
		{
			double my_x, my_y, my_z;
			if (pLUV[0] < 8.0)
				my_y = Yn * pLUV[0] / 903.3;
			else
				my_y = Yn * pow((pLUV[0] + 16.0) / 116.0, 3);

			double u_prime = pLUV[1] / (13 * pLUV[0]) + Un_prime;
			double v_prime = pLUV[2] / (13 * pLUV[0]) + Vn_prime;

			my_x = 9 * u_prime * my_y / (4 * v_prime);
			my_z = (12 - 3 * u_prime - 20 * v_prime) * my_y / (4 * v_prime);

			pRGB[0] = int((R_G_B[0][0]*my_x + R_G_B[0][1]*my_y + R_G_B[0][2]*my_z)*255.0);
			pRGB[1] = int((R_G_B[1][0]*my_x + R_G_B[1][1]*my_y + R_G_B[1][2]*my_z)*255.0);
			pRGB[2] = int((R_G_B[2][0]*my_x + R_G_B[2][1]*my_y + R_G_B[2][2]*my_z)*255.0);

			for (i = 0; i < 3; i++)
			{
				if (pRGB[i] > 255) pRGB[i] = 255;
				else if (pRGB[i] < 0) pRGB[i] = 0;
			}
		}
    }
	else
    {
		pRGB[0] = (int)pLUV[0];
		pRGB[1] = (int)pLUV[1];
		pRGB[2] = (int)pLUV[2];
	}
}

template<class Tp>
void Raster_Color<Tp>::CreateLUVspace(void) // Jiadi
{
	convert_RGB_LUV();
}

template<class Tp>
void Raster_Color<Tp>::SetPixelInLUV(const int row, const int col,
							const int l, const int u, const int v) // Jiadi
{
	double	luv[3];
	int		rgb[3];

	luv[0] = double(l);
	luv[1] = double(u);
	luv[2] = double(v);

	convert_LUV_RGB(luv, rgb);
	R(row,col) = (Tp)rgb[0];
	G(row,col) = (Tp)rgb[1];
	B(row,col) = (Tp)rgb[2];
}

template<class Tp>
void Raster_Color<Tp>::MeanVariance(Matrix<int> &mask_label, irmm_Rect &bound,
								 const int num_label,
								 Matrix<double>	*pmeans,
								 Matrix<double> *pvariances,
								 Matrix<long> &counts) // Jiadi
{
	int i,j,m,n,k;

	if (pmeans == NULL) return;
	long *pnum_pixel=new long[num_label];

	for (i=0; i<num_label; i++)
	{
		pnum_pixel[i] = 0;
		pmeans[i].SetDimension(3,1);
		pmeans[i].InitValue(0.0);
		if (pvariances != NULL)
		{
			pvariances[i].SetDimension(3,3);
			pvariances[i].InitValue(0.0);
		}
	}
	counts.SetDimension(num_label,1);

	// compute the mean value
	for (j=bound.top; j<=bound.bottom; j++)
		for (i=bound.left; i<=bound.right; i++)
		{
			if (mask_label(j,i)>0 && mask_label(j,i)<=num_label)
			{
				k = mask_label(j,i)-1;
				pnum_pixel[k]++;
				pmeans[k](0) += (double)L(j,i);
				pmeans[k](1) += (double)U(j,i);
				pmeans[k](2) += (double)V(j,i);
			}
		}
	for (i=0; i<num_label; i++)
	{
		if (pnum_pixel[i]>0)
		{
			pmeans[i](0) = pmeans[i](0)/((double)pnum_pixel[i]);
			pmeans[i](1) = pmeans[i](1)/((double)pnum_pixel[i]);
			pmeans[i](2) = pmeans[i](2)/((double)pnum_pixel[i]);
		}
		else
		{
			pmeans[i](0) = 0.0;
			pmeans[i](1) = 0.0;
			pmeans[i](2) = 0.0;
		}
	}

	if (pvariances != NULL)
	{
		for (j=bound.top; j<=bound.bottom; j++)
			for (i=bound.left; i<=bound.right; i++)
			{
				if (mask_label(j,i)>0 && mask_label(j,i)<=num_label)
				{
					k = mask_label(j,i)-1;
					for (n=0; n<3; n++)
					for (m = 0; m < 3; m++)
						pvariances[k](n, m) += ((double)(LUV.at<cv::Vec3i>(j, i)[n] - pmeans[k](n)))*
						((double)(LUV.at<cv::Vec3i>(j, i)[m] - pmeans[k](m)));
				}
			}
		for (i=0; i<num_label; i++)
		{
			for (n=0; n<3; n++)
				for (m=0; m<3; m++)
				{
					if (pnum_pixel[i]>1)
						pvariances[i](n,m) = pvariances[i](n,m)/(pnum_pixel[i]-1);
				}
			counts(i) = (long)pnum_pixel[i];
		}
		// make sure the determinate of covariance matrix is not zero
		for (i=0; i<num_label; i++)
		{
			if (pvariances[i].Det()==0)
				pvariances[i].Identity();
		}
	}
	delete []pnum_pixel;
}

template<class Tp>
void Raster_Color<Tp>::Compute2DColor(Matrix<int> &mask_label, irmm_Rect &bound,const int num_label, Vector<double> *pvc_N) // Jiadi
{
	int				i,j,m,k;
	double			din;
	Vector<double>	vc_weights,vc_temp(3);

	if (pvc_N == NULL) return;

	for (i=0; i<num_label; i++)
	{
		pvc_N[i].SetRowVector(3);
		pvc_N[i].InitValue(0.0);
	}
	vc_weights.SetRowVector(num_label);
	vc_weights.InitValue(0.0);

	// compute the major direction
	for (j=bound.top; j<=bound.bottom; j++)
		for (i=bound.left; i<=bound.right; i++)
		{
			if (mask_label(j,i)>=0 && mask_label(j,i)<num_label)
			{
				k = mask_label(j,i);

				vc_temp(0) = (double)R(j,i);
				vc_temp(1) = (double)G(j,i);
				vc_temp(2) = (double)B(j,i);

				// get intensity
				din = vc_temp.Norm(2.0);
				// normalize it
				if (din>0.0)
				{
					for (m=0; m<3; m++)
					{
						vc_temp(m) = vc_temp(m)/din;
						pvc_N[k](m) = (pvc_N[k](m)*vc_weights(k)+din*vc_temp(m))/(vc_weights(k)+din);
					}
					vc_weights(k) += din;
				}
			}
		}

	for (k=0; k<num_label; k++)
	{
		din = pvc_N[k].Norm(2.0);
		if (din>0.0)
			pvc_N[k] = pvc_N[k]/din;
	}
}

template<class Tp>
void Raster_Color<Tp>::GrayImage(Raster<Tp> &image) // Jiadi
{
	image.SetDimension(rows(), cols());
	cvtColor(RGB, image.data, CV_RGB2GRAY);
}

template<class Tp>
bool Raster_Color<Tp>::IsColorImage() // Jiadi
{

	int	i, j;
	Tp			byR,byG,byB;
	for (j=0; j<_rows; j++)
		for (i=0; i<_cols; i++)
		{
			byR = R(j, i);
			byG = G(j, i);
			byB = B(j, i);

			if (abs(byR-byG)>5 || abs(byR-byB)>5 ||abs(byG-byB)>5)
				return true;	// any three components in color are different
		}

	return false;
}

//end by wtf

#endif
